package net.ikenway.springboot.demo.oauth;

import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;

/**
 * @author MasterKenway <zk@ikenway.net>
 * @Description
 * @date Created Date 12/3/2020
 */
@EnableWebSecurity
@Configuration
public class SpringbootSecurityApplication {
    @Configuration
    static class UserSecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        UserDetailsService CustomUserDetailsService() {
            return new InMemoryUserDetailsManager(User.withUsername("kenway").password("123456").roles("RoleA").authorities("USER").build());
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            super.configure(auth);
        }

        @Override
        public void configure(WebSecurity web) {
            web.ignoring().antMatchers("/css/**", "/img/**", "/**/*.png");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // @formatter:off
            // 不指定path,本安全过滤链会匹配所有请求。
            http
                    .authorizeRequests()
                        .antMatchers("/").permitAll()// 首页放行
                    .anyRequest().hasAnyAuthority("USER").and()
                    .formLogin()
                        .loginPage("/oauth/github").permitAll()
                        .defaultSuccessUrl("/user").and()
                    .logout()
                        .logoutUrl("/logout").permitAll()
                        .logoutSuccessUrl("/user?logout").and()
                    // 自定义访问拒绝异常处理逻辑
                    // 使用 tony/tony 账号测试权限不符合的返回信息。
                    .exceptionHandling()
                        .accessDeniedHandler(UserSecurityConfig::accessDeniedHandle).and()
                    .apply(new GithubOAuth2LoginConfigurer<>())
            ;

            // @formatter:on
        }

        /**
         * @see AccessDeniedHandlerImpl#handle(HttpServletRequest, HttpServletResponse, AccessDeniedException)
         */
        private static void accessDeniedHandle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
            httpServletRequest.setAttribute(WebAttributes.ACCESS_DENIED_403,
                    accessDeniedException);
            response.setStatus(HttpStatus.FORBIDDEN.value());
            response.setCharacterEncoding(Charset.defaultCharset().displayName());// 解决中文乱码
            response.addHeader("Content-Type", MediaType.TEXT_HTML_VALUE);
            response.getWriter().write("你的权限不够");
        }

    }
}
